#import "JVMonitorCenter.h"
#import <sys/time.h>
#import <UIKit/UIKit.h>
#import "JVNetworkReachability.h"
#import "UIDevice+Hardware.h"
#import "ALBaseConstants.h"
#import "AFURLSessionManager.h"
#import "AFHTTPSessionManager.h"
#import "JVMonitorConfig.h"
#import "JVMonitorCenterHelper.h"
#import "ALDeviceInfoTools.h"
#import "MonitorDBManager.h"
#import "MonitorDBModel.h"
#import "ALUtils.h"

#define CAT_DATABASE_TABLE_NAME @"CAT"

@interface JVMonitorCenter ()

@property (nonatomic, strong) MonitorDBManager *dbHelperInstance;
//客户端版本
@property (nonatomic, assign) NSInteger versionCode;
//最大上传条数
@property (nonatomic, assign) NSInteger maxUploadCount;
//flush最大上传条数 退到后台一次性上传多条
@property (nonatomic, assign) NSInteger maxFlushUploadCount;


//上传的gcd处理队列
@property (nonatomic, strong) dispatch_queue_t goal_serial_queue;
//信号量，作为锁
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@end

/**
 http://lujs.cn/confluence/pages/viewpage.action?pageId=347931066
 ****************************************************************
 公共字段说明（公共字段中的 L 字段）
 字段名    意义                            附加说明
 ut       消息包上传时间                    unix 时间戳
 cid      客户端 ID                        客户端唯一标示设备 ID
 app      应用名称                          默认为 lufax
 p        平台或者操作系统	比如               ios, android 等
 pv       平台或者操作系统版本                比如 android 7.0 等
 d        设备或者手机名称                   比如小米、华为等
 dv       设备或者手机型号                   比如小米 note4 等
 b        游览器分类                        比如 chrome, firefox 等
 bv       游览器型号                        比如 chrome 40 等
 w        网络视图分类                      比如 chromium
 wv       网络视图型号                      比如 chromium 37
 l        统计值列表                        将通用字段提取出来，便于节省空间
 ****************************************************************
 */
@interface JVPharosModel : NSObject

@property (nonatomic, assign) uint64_t ut;          //": 1484536179728, // 批量上传的时间戳，毫秒
@property (nonatomic, copy) NSString *cid;          //": "client id" // client id
@property (nonatomic, copy) NSString *app;          //": "lufax", // 应用名称(长度不超过20个字符)
@property (nonatomic, copy) NSString *p;            //": "i", // 平台，比如 android，ios 等，英文
@property (nonatomic, copy) NSString *pv;           //": "ios10.1", // 平台版本，比如 ios10.1 等，英文

@property (nonatomic, copy) NSString *av;           //": "3.3.7", // APP 版本（非中文）
@property (nonatomic, copy) NSString *d;            //": "apple", // 设备名称，比如 apple，英文
@property (nonatomic, copy) NSString *dv;           //": "iphone7s" // 具体的手机型号，比如 iphone7s，英文
@property (nonatomic, strong) NSMutableArray<JVPharosRootModel *> *l;

/**
 "sp": {
 "f": "frequency",
 "c": "core",
 "m": "memory",
 "l": "launch time"
 },
 */
@property (nonatomic, copy) NSDictionary *sp;

- (NSString *)toString;

@end

@implementation JVPharosModel

- (instancetype)init
{
    self = [super init];
    if (self) {
        id<JVMonitorConfigDelegate> config = [JVMonitorConfig sharedInstance].monitorConfigDelegate;
        _cid = config.clientId;
        _app =config.appName;
        _av = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
        _p = @"ios";
        _pv = [ALDeviceInfoTools OSVersion];
        _d = [ALDeviceInfoTools OSName];
        _dv = [[UIDevice currentDevice] hardwareSimpleDescription];
        _sp = [ALDeviceInfoTools logSP];
    }
    return self;
}
- (NSString *)toString{
    NSDictionary *dic = @{
        @"ut":@(self.ut)?:@((uint64_t)([[NSDate date] timeIntervalSince1970]*NSEC_PER_USEC)),
        @"cid":self.cid?:@"",
        @"app":self.app?:@"null",
        @"av":self.av?:@"",
        @"p":self.p?:@"",
        @"pv":self.pv?:@"",
        @"d":self.d?:@"",
        @"dv":self.dv?:@"",
        @"l":self.l?:@[]};
    NSString *jsonStr = JSON_STRING_FROM_DIC(dic);
    return jsonStr;
}

@end


@implementation JVMonitorCenter {
    //日志记录的URL
    NSURL *_serverURL;
    //    NSMutableArray *_buffer;
    NSNumber *_currentCount;
    //    NSNumber *_currentLoganCount;
    LUIDBlock _LUIDBlock;
    NSArray* filterArray;
    NSSet* filterSet;
    AFHTTPSessionManager* manager;
}

+ (instancetype)defaultCenter {
    static JVMonitorCenter *gCenter = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        gCenter = [JVMonitorCenter new];
    });
    return gCenter;
}

- (id)init {
    self = [super init];
    if (self) {
        self.goal_serial_queue = dispatch_queue_create("com.arlene.goal.queue.serial", DISPATCH_QUEUE_SERIAL);
        self.semaphore = dispatch_semaphore_create(1);
        self.maxUploadCount = 15;
        self.maxFlushUploadCount = 100;
        _currentCount = @0;
        self.dbHelperInstance = [MonitorDBManager shareInstance];
        self.versionCode = [self getVersionCode];
        
        //        [self searchRemainData];
        
        
        
        //        [self setServerUrl:[NSString stringWithFormat:@"%@/broker-service/api/batch",CAT_URL]];
        NSString *catRemoteAdderss = [[JVMonitorConfig sharedInstance].monitorConfigDelegate catRemoteAddress];
        [self setServerUrl:catRemoteAdderss];
        //        [self setServerUrl:@"https://mcat.lu.com/pharos/a/log"];
        manager = [AFHTTPSessionManager manager];
        //[manager.requestSerializer setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
        [manager setResponseSerializer:[AFHTTPResponseSerializer new]];
        if ([[JVMonitorConfig sharedInstance].monitorConfigDelegate catBackDoor]) {
            AFSecurityPolicy *_securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
            [_securityPolicy setAllowInvalidCertificates:YES];
            [_securityPolicy setValidatesDomainName:NO];
        }else{
            AFSecurityPolicy *_securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
            [_securityPolicy setAllowInvalidCertificates:NO];
            [_securityPolicy setValidatesDomainName:YES];
        }
        
        [manager setRequestSerializer:[AFHTTPRequestSerializer new]];
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
        /*通过<<cat_gen_commands.py>>生成*/
        filterArray = [[JVMonitorConfig sharedInstance].monitorConfigDelegate whitelist];
        filterSet = [NSSet setWithArray:filterArray];
    }
    return self;
}

- (BOOL)hasBackDoor{
    // 设置TodayWidget groupID
    //    if (groupIDStr.length == 0) {
    //        NSDictionary * infoDict = [[NSBundle mainBundle] infoDictionary];
    //        groupIDStr = [infoDict objectForKey:@"CFTodayWidgetGroupIDStr"];
    //    }
    //
    //    NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:groupIDStr];
    //    NSNumber *httpsDate = [ud objectForKey:@"HTTPS_BACKDOOR_DATE"];
    //    if (!httpsDate) {
    //        return NO;
    //    }
    //    // 获取当前时间戳
    //    NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];
    //    NSTimeInterval timestamp=[date timeIntervalSince1970];
    //    long timestamp_c = ceil(timestamp);
    //    long timestamp_l = [httpsDate longValue];
    //    if ((timestamp_c - timestamp_l) > 259200) {
    //        return NO;
    //    }else{
    //        return YES;
    //    }
    return NO;
}

- (void)applicationDidEnterBackground:(NSNotification *)n {
    @try {
        // 统计数据立即上传
        [self flush];
    }
    @catch (NSException *exception) {
        ALLOGF(@"applicationDidEnterBackground, forceUpload occured exception: %@", exception);
    }
}

- (void)setServerUrl:(NSString *)url {
    if (url.length<1) {
        _serverURL = nil;
    } else {
        _serverURL = [NSURL URLWithString:url];
    }
}

// 6.8 = 680, 6.8.5 = 685
- (NSInteger)getVersionCode {
    NSString * build = [[NSBundle mainBundle] objectForInfoDictionaryKey: (NSString *)kCFBundleVersionKey];
    NSScanner *scan = [NSScanner scannerWithString:build];
    NSInteger a=1, b=0, c=0; // aabbc
    do {
        if(![scan scanInt:(int *)&a]) {
            break;
        }
        if(![scan scanString:@"." intoString:NULL]) {
            break;
        }
        if(![scan scanInt:(int *)&b]) {
            break;
        }
        if(![scan scanString:@"." intoString:NULL]) {
            break;
        }
        [scan scanInt:(int *)&c];
    } while(0);
    return a*100 + (b%10)*10 + (c%10);
}


- (void)searchRemainData {
    NSArray *remainData = [self.dbHelperInstance searchDataWithDomain:CAT_DATABASE_TABLE_NAME deleteData:NO];
    ALLOGF(@"%@",remainData);
}

- (void)pvSaveWithPharosModel:(JVPharosRootModel *)pharosModel
{
    NSDictionary *pharosModel_dic = [pharosModel pharosModeToDic];
    //    NSDictionary *pharosModel_dic = [self pharosModeToDic:pharosModel];
    //    NSString *pharosModel_str = [Utility stringFromDictionary:pharosModel_dic?:@{} withOption:0];
    
    //    DLog(@"CAT: %@", pharosModel_str);
    // 0：全部不上传； 1：过白名单； 2：全部上传；
    LFXCatSwitchLevel catSwitchLevel = [[JVMonitorConfig sharedInstance].monitorConfigDelegate catUploadSwitchLevel];
    BOOL isNetwork = [[pharosModel type] isEqualToString:@"request"];
    
    switch (catSwitchLevel) {
        case LFXCatSwitchLevel0:
            return;
        case LFXCatSwitchLevel1:{
            if (isNetwork) {
                //白名单过滤
                NSString *url = [pharosModel commandId];//pharosModel.url;
                if (
                    !url ||
                    !filterSet ||
                    ![filterSet containsObject:url]) {
                    return;
                }
            }
        }break;
        case LFXCatSwitchLevel2:
            break;
        default:{
            if (isNetwork) {
                //白名单过滤
                NSString *url = [pharosModel commandId];//pharosModel.url;
                if (
                    !url ||
                    !filterSet ||
                    ![filterSet containsObject:url]) {
                    return;
                }
            }
        }break;
    }
    
    @synchronized (self) {
        // pharosModel_dic->pharosModel_str
        NSString *pharosModel_str = JSON_STRING_FROM_DIC(pharosModel_dic);
        //        int64_t index = [self.dbHelperInstance insertData:pharosModel_str domain:CAT_DATABASE_TABLE_NAME];
        int64_t index = [self.dbHelperInstance insertData:pharosModel_str];
        _currentCount = [NSNumber numberWithInteger:[_currentCount integerValue]+1];
        ALLOGF(@"CAT上传=========插入序列%lld",index);
    }
}

- (void)track:(JVPharosRootModel *)pharosModel {
    [self pvSaveWithPharosModel:pharosModel];
    if ([_currentCount integerValue]/self.maxUploadCount>=1) {
        ALLOGF(@"CAT上传=========满足%ld条",self.maxUploadCount);
        [self uploadData:NO];
    }
}

- (NSString *)getBaseURL:(NSString *)url {
    return @"cat.arlene.com";
}


- (void)flush {
    [self uploadData:YES];
}

- (NSString *)urlEncode:(NSString *)url {
    if (![url length]) return @"";
    CFStringRef static const charsToEscape = CFSTR("!*'();:@&=+$,/?%#[]");
    CFStringRef escapedString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                                        (__bridge CFStringRef)url,
                                                                        NULL,
                                                                        charsToEscape,
                                                                        kCFStringEncodingUTF8);
    return (__bridge_transfer NSString *)escapedString;
}
// flush是否全部上传，NO代表上传maxUploadCount
- (void)uploadData:(BOOL)flush {
    dispatch_async(self.goal_serial_queue, ^{
        ALLOGF(@"CAT上传=========进入block===wait");
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        ALLOGF(@"CAT上传=========进入block===signal");
        NSString *body = @"";
        
        // 取出15条
        NSArray *log_from_db = nil;
        if (!flush) {
            log_from_db = [self.dbHelperInstance searchDataWithDomain:CAT_DATABASE_TABLE_NAME deleteData:NO count:self.maxUploadCount];
        }else {
            log_from_db = [self.dbHelperInstance searchDataWithDomain:CAT_DATABASE_TABLE_NAME deleteData:NO count:self.maxFlushUploadCount];
        }
        NSMutableArray *index_array = [NSMutableArray array];
        NSMutableArray *log_data_array = [NSMutableArray array];
        ALLOGF(@"CAT上传=========取出%ld条准备上传",[log_from_db count]);
        for (int i = 0; i < [log_from_db count]; i++) {
            MonitorDBModel* record_item = (MonitorDBModel*)[log_from_db objectAtIndex:i];
            [index_array addObject:[NSNumber numberWithInteger:record_item.index]];
            // 数据转换NSStirng->NSDictionary
            NSString *item_data_str = record_item.record;
            if (item_data_str != nil && [item_data_str isKindOfClass:[NSString class]]) {
                NSError * jsonError = nil;
                NSDictionary * item_data_dic = [NSJSONSerialization JSONObjectWithData:[item_data_str dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&jsonError];
                [log_data_array addObject:item_data_dic?:@{}];
            }
        }
        
        //        NSDate *currentDate = [[LFSynchronizer instance] checkedTime];
        NSDate* currentDate = [NSDate date];
        JVPharosModel *pharosModel = [[JVPharosModel alloc] init];
        pharosModel.ut = (uint64_t)([currentDate timeIntervalSince1970]*NSEC_PER_USEC);
        pharosModel.l = log_data_array;
        body = [pharosModel toString];
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:_serverURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60];
        [request setHTTPMethod:@"POST"];
        [request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
        [request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
        [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        
        NSData *bodyData = [body dataUsingEncoding:NSUTF8StringEncoding];
        NSString *postLength = [NSString stringWithFormat:@"%ld", (unsigned long)[bodyData length]];
        [request setValue:postLength forHTTPHeaderField:@"content-Length"];
        
        NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request
                                                                   fromData:bodyData
                                                                   progress:nil
                                                          completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
            if (error) {
                // do nothing
                ALLOGF(@"CAT上传=========上传失败");
            } else {
                ALLOGF(@"CAT上传=========成功");
                NSNumber *fromIndex = [index_array firstObject];
                NSNumber *toIndex = [index_array lastObject];
                [self.dbHelperInstance clearDataFromIndex:[fromIndex longLongValue] toIndex:[toIndex longLongValue]];
            }
            _currentCount = [NSNumber numberWithLongLong:0];
            dispatch_semaphore_signal(self.semaphore);
        }];
        [uploadTask resume];
    });
}


-(void)trackRealTime:(JVPharosRootModel *)rootModel{
    NSMutableArray *log_data_array = [NSMutableArray array];
    NSDictionary* dic = [rootModel pharosModeToDic];
    [log_data_array addObject:dic];
    NSDate* currentDate = [NSDate date];
    JVPharosModel *pharosModel = [[JVPharosModel alloc] init];
    pharosModel.ut = (uint64_t)([currentDate timeIntervalSince1970]*NSEC_PER_USEC);
    pharosModel.l =log_data_array;
    NSString *body = [pharosModel toString];
    
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:_serverURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
    [request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    
    NSData *bodyData = [body dataUsingEncoding:NSUTF8StringEncoding];
    NSString *postLength = [NSString stringWithFormat:@"%ld", (unsigned long)[bodyData length]];
    [request setValue:postLength forHTTPHeaderField:@"content-Length"];
    
    NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request
                                                               fromData:bodyData
                                                               progress:nil
                                                      completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
        if (error) {
            ALLOGF(@"CAT上传=========上传失败");
        } else {
            ALLOGF(@"CAT上传=========成功");
        }
    }];
    [uploadTask resume];
}
@end
